chore: child to parent singleton bleed#14
Draft
brunozoric wants to merge 8 commits into
Draft
Conversation
Describes the singleton bleed-through bug where child container registrations pollute a parent's cached singleton, and the fix: cache singletons per-resolving-container instead of per-owning-container. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds singleton variants of all childContainer.test.ts scenarios to ensure cross-resolution behavior is bulletproof under singleton scoping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add child decorator chain application to the singleton scoping fix - Add ProductRegistry example with parent/child/grandchild showing before/after behavior with 6 products and 2 decorators - Add registry.test.ts to impacted test list (found by code review) - Add 6 additional test scenarios: decorator chain, resolution ordering, singleton dependency chains - Document as breaking semantic change requiring semver bump Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 tasks covering: failing tests, Container.ts fix, existing test updates, singleton cross-resolution tests, decorator chain tests, and documentation updates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cold review findings: falsy singleton cache bug, reflect-metadata
side-effect in types.ts, circular { multiple: true } stack overflow,
dead prettier scripts, undocumented composite/resolveAll behavior.
6 tasks covering: cache fix, reflect-metadata cleanup, prettier
removal, circular depth guard, builder fluency, AGENTS.md docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes singleton bleed-through in parent/child container hierarchies. When a singleton registered in a parent container depends on
{ multiple: true }abstractions, child container registrations currently leak into the parent's cached singleton instance — and from there into every other container in the hierarchy.Root cause:
resolveRegistrationcaches singletons inthis.instances(the owning container) but resolves dependencies viaresolveFrom(the requesting container). A child resolving a parent-owned singleton collects child-scoped registrations, then writes the result into the parent's cache — polluting it for all subsequent resolvers.Fix: Per-container singleton scoping — cache singletons in
resolveFrom.instancesinstead ofthis.instances, and apply decorator chains in parent-to-child order so each container gets its own singleton instance reflecting its own dependency graph.What changes
Core (
src/Container.ts)resolveFrom.instancesinstead ofthis.instancesresolveFromup tothisand apply each intermediate container's decorators in parent-to-child orderif (existing)toif (existing !== undefined)so singletons resolving to0,false,"", ornullare correctly cachedNew singleton semantics
Tests
singletons.test.ts,registry.test.ts: cross-containertoBeidentity assertions updated to assert behavioral equivalence (same deps, same values) within the same containersingletonBleed.test.ts— bleed-through prevention, child inheritance, sibling isolation, deep hierarchy, same-container identitysingletonCrossResolution.test.ts— singleton variants of allchildContainer.test.tsscenarios (override some/all/no deps, grandchild chain, great-grandchild 4-level hierarchy)singletonDecoratorChain.test.ts— child/grandchild decorator application, parent isolation from child decoratorsDocs
AGENTS.mdlifetime scopes section updated to reflect per-container singleton semanticsBreaking change
This is a breaking semantic change. The singleton contract changes from "one shared instance across the hierarchy" to "one instance per container that resolves it."
Code that relies on
child.resolve(X) === parent.resolve(X)(reference identity across containers) will break. Within the same container, reference identity is preserved.Design docs (on this branch)
docs/2026-05-26-per-container-singleton-scoping-design.md— full design spec with before/after code, decorator ordering, alternatives considereddocs/superpowers/plans/2026-05-26-per-container-singleton-scoping.md— task-by-task implementation plandocs/2026-05-26-container-hardening-design.md— cold review findings (falsy cache bug, circular dep check bypass, perf issues)Test plan
toBe-identical instance0,false,"",null) are cached correctly